MMAP(2) | Linux Programmer's Manual | MMAP(2) |
名前¶
mmap, munmap - ファイルやデバイスをメモリにマップ/アンマップする
書式¶
#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); int munmap(void *addr, size_t length);
説明¶
mmap() は、新しいマッピングを呼び出し元プロセスの仮想アドレス空間に作成する。 新しいマッピングの開始アドレスは addr で指定される。マッピングの長さは length 引き数で指定される。
addr が NULL の場合、カーネルがマッピングを作成するアドレスを選択する。 この方法は最も移植性のある新しいマッピングの作成方法である。 addr が NULL でない場合、カーネルはマッピングをどこに配置するかのヒントとして addr を使用する。Linux では、マッピングはすぐ近くのページ境界に作成される。 新しいマッピングのアドレスは、呼び出しの返り値として返される。
ファイルマッピングの内容は、 ファイルディスクリプタ fd で参照されるファイル (もしくは他のオブジェクト) のオフセット offset から開始される length バイトのデータで初期化される (ファイルマッピングは無名マッピングの反対語である。 MAP_ANONYMOUS を参照)。 offset は sysconf(_SC_PAGE_SIZE) が返すページサイズの倍数でなければならない。
引き数 prot には、マッピングのメモリ保護をどのように行なうかを指定する (ファイルのオープンモードと矛盾してはいけない)。 prot には、 PROT_NONE か、以下のフラグをひとつ以上ビット毎の論理和 (OR) をとったものを 指定できる。
- PROT_EXEC
- ページは実行可能である。
- PROT_READ
- ページは読み込み可能である。
- PROT_WRITE
- ページに書き込み可能である。
- PROT_NONE
- ページにはアクセスできない。
flags 引き数により、マッピングに対する更新が同じ領域をマッピングしている 他のプロセスに見えるか、更新がマッピング元のファイルを通じて 伝えられるか、が決定される。この動作は、以下の値のいずれか一つだけ (複数は指定できない) を flags に含めることで指定する。
- MAP_SHARED
- このマッピングを共有する。 マッピングに対する更新はこのファイルをマッピングしている他のプロセス から見える。更新はマッピング元のファイルを通じて伝えられる。 ただし、ファイルの実際の更新は msync(2) または munmap() が呼ばれるまで行われないこともある。
- MAP_PRIVATE
- プライベートな copy-on-write (書き込み時コピー) マップを生成する。 マッピングに対する更新は同じファイルをマッピングしている他のプロセス には見えず、更新がマッピング元のファイルを通じて伝えられることもない。 mmap() の呼び出し後にマッピング元のファイルに対して行われた変更が、 マップ領域に反映されるかどうかは規定されていない。
上記の二つのフラグは POSIX.1-2001 で規定されている。
さらに、以下の値のうち 0 個以上をビット毎の論理和 (OR) で flags に指定することができる。
- MAP_32BIT (Linux 2.4.20, 2.6 以降)
- マッピングをプロセスのアドレス空間の先頭 2 ギガバイト以内に配置する。 このフラグがサポートされているのは x86-64 アーキテクチャ上の 64 ビットプログラムのみである。 このフラグが追加されたのは、スレッドのスタックをメモリの先頭 2GB 以内の どこかに割り当てることで、初期のいくつかの 64 ビットプロセッサにおける コンテキストスイッチの性能問題を改善するためである。 最近の x86-64 プロセッサではこの性能問題はもはや存在せず、 そのようなシステムではこのフラグを使用する必要はない。 MAP_FIXED がセットされている場合は、 MAP_32BIT フラグは無視される。
- MAP_ANON
- MAP_ANONYMOUS の同義語。非推奨。
- MAP_ANONYMOUS
- マッピングはどのファイルとも関連付けされない。 マッピングの内容は 0 で初期化される。 引き数 fd と offset は無視される。 ただし、実装によっては MAP_ANONYMOUS (もしくは MAP_ANON) が指定された場合、 fd を -1 にする必要があり、 移植性が必要なアプリケーションでは必ず fd を -1 にすべきである。 MAP_ANONYMOUS と MAP_SHARED を組み合わせての利用は カーネル 2.4 以降の Linux でのみサポートされている。
- MAP_DENYWRITE
- このフラグは無視される (ずっと前は、マップ元のファイルへの書き込みを行おうとすると、エラー ETXTBUSY で失敗するようにシグナルが設定されていたが、これは denial-of-service (サービス拒否) 攻撃の原因となった)。
- MAP_EXECUTABLE
- このフラグは無視される。
- MAP_FILE
- 互換性のためのフラグ。無視される。
- MAP_FIXED
- addr をアドレスのヒントとして使用するのではなく、 addr で指定されたアドレスをそのまま使用してマッピングを配置する。 addr はページサイズの倍数でなければならない。 addr と len で指定されたメモリ領域が既存のマッピングのページと重なる場合、 既存のマッピングの重なった部分は捨てられる。 もし指定されたアドレスが使用できない場合、 mmap() は失敗する。 マッピングに対して固定アドレスを要求するのは移植性の面で劣るので、 このオプションは使用しないことを推奨する。
- MAP_GROWSDOWN
- スタック用に使用される。マッピングをメモリ内で逆向きに行うことを カーネル仮想メモリシステムに指示する。 (訳注:マッピングは通常はメモリ・アドレスが増加する向きに行うが、 このオプションを指定すると逆向きにマッピングを行う)
- MAP_LOCKED (Linux 2.5.37 以降)
- マップされた領域のページを mlock(2) の方法でメモリ内にロックする。 それ以前のカーネルでは、このフラグは無視される。
- MAP_NONBLOCK (Linux 2.5.46 以降)
- MAP_POPULATE と組み合わせた場合のみ意味を持つ。 read-ahead (前もって読み込むこと) を実行しない。 単に、すでに RAM 上に存在するページに対して ページテーブルエントリを作成するだけである。 Linux 2.6.23 以降では、このフラグは MAP_POPULATE に何の影響も与えない。 いつか MAP_POPULATE と MAP_NONBLOCK を組み合わせた場合の動作は実装し直されるかもしれない。
- MAP_NORESERVE
- このマッピングに対するスワップ空間の予約を行わない。 スワップ空間を予約した場合は、このマッピングの変更が必ず可能なことが 保証される。予約を行わなかった場合、物理メモリに空きがないと 書き込み時に SIGSEGV エラーを受け取ることがある。 proc(5) の /proc/sys/vm/overcommit_memory ファイルについての議論も参照。 バージョン 2.6 より前のカーネルでは、このフラグは書き込み可能な プライベート・マッピングについてのみ効果があった。
- MAP_POPULATE (Linux 2.5.46 以降)
- マッピング用のページテーブルを配置 (populate) する ファイルマッピングの場合には、これによりファイルが先読み (read-ahead) が行われる。この以後は、マッピングに対するアクセスがページフォールトで ブロックされることがなくなる。 MAP_POPULATE は Linux 2.6.23 以降でプライベート・マッピングについてのみ サポートされている。
上記のフラグの中では、 MAP_FIXED だけが POSIX.1-2001 で規定されている。 しかしながら、ほとんどのシステムで MAP_ANONYMOUS (またはその同義語である MAP_ANON) もサポートされている。
- MAP_STACK (Linux 2.6.27 以降)
- プロセスやスレッドのスタックに適したアドレスにマッピングを割り当てる。 現在のところ、このフラグは何もしないが、 glibc のスレッド実装では使用されている。 これは、いくつかのアーキテクチャではスタックの割り当てに関して特別な扱い が必要な場合に、glibc にそのサポートを後で透過的に実装できるようにする ためである。
いくつかのシステムでは、上記以外にフラグとして MAP_AUTOGROW, MAP_AUTORESRV, MAP_COPY, MAP_LOCAL が規定されている。
mmap() によってマップされたメモリの属性は fork(2) の際に継承される。
ファイルはページサイズの整数倍の領域にマップされる。サイズがページサイズの 整数倍でないファイルの場合、マップ時に残りの領域は 0 で埋められ、この領域へ 書きこみを行ってもファイルに書き出されることはない。マッピングを行った元 ファイルのサイズを変更した場合、元ファイルの追加されたり削除された領域に対応 するマップされたページに対してどのような影響があるかは規定されていない。
munmap()¶
システムコール munmap() は指定されたアドレス範囲のマップを消去し、 これ以降のその範囲内へのメモリ参照は不正となる。 この領域は、プロセスが終了したときにも自動的にアンマップされる。 一方、ファイル記述子をクローズしても、この領域はアンマップされない。
addr アドレスはページサイズの整数倍でなければならない。指定された範囲の一部分を 含む全てのページはアンマップされ、これ以降にこれらのページへの参照があると SIGSEGV が発生する。 指定した範囲内にマップされたページが一つも含まれていない場合でも エラーにならない。
ファイルと関連付けられたマッピングに対するタイムスタンプの更新¶
ファイルと関連付けられたマッピングの場合、マッピングされたファイルの st_atime フィールドは、 mmap() されてからアンマップ (unmap) されるまでの間に更新されることがある。 それまでに更新が行われていなければ、マップされたページへの最初の参照があった 際に更新される。
PROT_WRITE と MAP_SHARED の両方を指定してマップされたファイルの場合、書き込みがあると、 st_ctime と st_mtime の両フィールドは、マップされた領域への書き込みより後で、 MS_SYNC または MS_ASYNC フラグを指定して msync(2) が呼ばれる前までに更新される。
返り値¶
mmap() は成功するとマップされた領域へのポインタを返す。 失敗すると値 MAP_FAILED (つまり (void *) -1) を返し、 errno がエラーの内容にしたがってセットされる。 munmap() は成功すると 0 を返す。失敗すると -1 を返し、 errno がセットされる (多くの場合 EINVAL になるだろう)。
エラー¶
- EACCES
- 以下のいずれかの場合。 ファイル記述子の参照先が通常のファイルではない (non-regular file) 。 MAP_PRIVATE を要求したが fd は読み込み用にオープンされていない。 MAP_SHARED を要求して PROT_WRITE をセットしたが fd は読み書きモード (O_RDWR) でオープンされていない、 PROT_WRITE をセットしたが、ファイルは追加 (append) 専用である。
- EAGAIN
- ファイルがロックされている。またはロックされているメモリが多すぎる (setrlimit(2) を参照)。
- EBADF
- fd が有効なファイル記述子 (file descriptor) ではない (かつ MAP_ANONYMOUS がセットされていない)。
- EINVAL
- addr か length か offset が適切でない (例えば、大きすぎるとか、ページ境界にアラインメントされていない)。
- EINVAL
- (Linux 2.6.12 以降) length が 0 であった。
- EINVAL
- flags に MAP_PRIVATE と MAP_SHARED のどちらも含まれていなかった、もしくは その両方が含まれていた。
- ENFILE
- オープンされたファイルの総数がシステムの制限に達した。
- ENODEV
- 指定されたファイルが置かれているファイルシステムがメモリマッピングをサポート していない。
- ENOMEM
- メモリに空きがない、または処理中のプロセスのマッピング数が最大数を超過した。
- EPERM
- prot 引き数は PROT_EXEC を行うように指定されているが、 no-exec でマウントされたファイルシステム上のファイルに マップ領域が対応している。
- ETXTBSY
- MAP_DENYWRITE がセットされているが fd で指定されているオブジェクトは書き込み用に開かれている。
マップ領域を利用する際に、以下のシグナルが発生することがある:
準拠¶
SVr4, 4.4BSD, POSIX.1-2001.
可用性¶
mmap(), msync(2) munmap() が利用可能な POSIX システムでは、 _POSIX_MAPPED_FILES は <unistd.h> で 0 より大きな値に定義される (sysconf(3) も参照のこと)。
注意¶
カーネル 2.4 以降、このシステムコールは mmap2(2) に取って代わられた。 現在では、 glibc の mmap() のラッパー関数は offset を適切に調整してから mmap2(2) を起動する。
(i386 などの) いくつかのアーキテクチャでは、 PROT_WRITE をセットすると、暗黙のうちに PROT_READ がセットされる。 PROT_READ をセットした際に暗黙のうちに PROT_EXEC がセットされるかどうかは、アーキテクチャ依存である。 移植性を考慮したプログラムでは、 新規にマップした領域でコードを実行したい場合は、常に PROT_EXEC をセットすべきである。
マッピングを作成する移植性のある方法は、 addr に 0 (NULL) を指定し、 flags から MAP_FIXED を外すことである。 この場合、システムがマッピング用のアドレスの選択を行う。 アドレスは既存のマッピングと衝突しないように、 かつ 0 にならないように選択される。 MAP_FIXED フラグが指定され、かつ addr が 0 (NULL) の場合には、マップされるアドレスが 0 (NULL) になる。
バグ¶
Linux においては、上記の MAP_NORESERVE で述べられているような保証はない。 デフォルトでは、システムがメモリを使い切った場合には、 どのプロセスがいつ強制終了されるか分からないからである。
2.6.7 より前のカーネルでは、 prot に PROT_NONE が指定された場合にのみ、 MAP_POPULATE フラグが効力を持つ。
SUSv3 では、 length が 0 の場合、 mmap() は失敗すると規定されている。しかしながら、2.6.12 より前のカーネルでは、 この場合に mmap() は成功していた (マッピングは作成されず、 addr が返されていた)。 カーネル 2.6.12 以降では、 mmap() はエラー EINVAL で失敗する。
例¶
以下のプログラムは、一番目のコマンドライン引き数で指定された ファイルの一部を標準出力に表示する。 表示する範囲は、二番目、三番目のコマンドライン引き数で渡される オフセットと長さで指定される。 このプログラムは、指定されたファイルの必要なページのメモリ・ マッピングを作成し、 write(2) を使って所望のバイトを出力する。
#include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0) int main(int argc, char *argv[]) {
char *addr;
int fd;
struct stat sb;
off_t offset, pa_offset;
size_t length;
ssize_t s;
if (argc < 3 || argc > 4) {
fprintf(stderr, "%s file offset [length]\n", argv[0]);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_RDONLY);
if (fd == -1)
handle_error("open");
if (fstat(fd, &sb) == -1) /* To obtain file size */
handle_error("fstat");
offset = atoi(argv[2]);
pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1);
/* offset for mmap() must be page aligned */
if (offset >= sb.st_size) {
fprintf(stderr, "offset is past end of file\n");
exit(EXIT_FAILURE);
}
if (argc == 4) {
length = atoi(argv[3]);
if (offset + length > sb.st_size)
length = sb.st_size - offset;
/* Can't display bytes past end of file */
} else { /* No length arg ==> display to end of file */
length = sb.st_size - offset;
}
addr = mmap(NULL, length + offset - pa_offset, PROT_READ,
MAP_PRIVATE, fd, pa_offset);
if (addr == MAP_FAILED)
handle_error("mmap");
s = write(STDOUT_FILENO, addr + offset - pa_offset, length);
if (s != length) {
if (s == -1)
handle_error("write");
fprintf(stderr, "partial write");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS); } /* main */
関連項目¶
getpagesize(2), mincore(2), mlock(2),
mmap2(2), mprotect(2), mremap(2), msync(2),
remap_file_pages(2), setrlimit(2), shmat(2),
shm_open(3), shm_overview(7)
B.O. Gallmeister, POSIX.4, O'Reilly, pp. 128-129 and 389-391.
2008-12-01 | Linux |